#include <iostream>
#include <unordered_map>
#include <functional>
#include <cassert>
#include <sys/epoll.h>

#include "Sock.hpp"
#include "log.hpp"
#include "err.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

class Connection;
class EpollServer;

const static uint16_t defaultport = 8888;

using namespace protocol_ns;

using func_t = std::function<void(Connection *, const Request &)>;
using callback_t = std::function<void(Connection *)>;

// 连接信息，包括文件描述符，缓冲区，关心事件等
class Connection
{
public:
    Connection(const int &fd, const std::string &clientIp, const uint16_t clientPort)
        : _fd(fd),
          _clientIp(clientIp),
          _clientPort(clientPort)
    {
    }

    // 绑定I/O处理函数
    void Rigster(callback_t recv, callback_t send, callback_t except)
    {
        _recv = recv;
        _send = send;
        _except = except;
    }

public:
    // 连接信息
    int _fd;
    uint32_t _events;
    std::string inbuffer;
    std::string outbuffer;

    // I/O处理函数
    callback_t _recv;
    callback_t _send;
    callback_t _except;

    // 用户信息
    std::string _clientIp;
    uint16_t _clientPort;

    // 回指指针
    EpollServer *_R;
};

class EpollServer
{
    const static int gnum = 64;

public:
    EpollServer(func_t func, uint16_t port = defaultport) : _func(func), _port(port)
    {
    }
    void InitServer()
    {
        _listensock.Socket();    // 创建套接字
        _listensock.Bind(_port); // 绑定IP，端口号
        _listensock.Listen();    // 开启监听连接
        _epoller.Create();       // 创建epoll模型
        // 将监听套接字添加到连接管理和epoll模型中
        AddConnection(_listensock.Fd(), EPOLLIN | EPOLLET);
    }

    // 事件派发器
    void Dispatcher()
    {
        int timeout = 100000;
        while (true)
        {
            LoopOnce(timeout);
        }
    }
    // 处理就绪的事件
    void LoopOnce(int timeout)
    {
        int n = _epoller.Wait(_revs, gnum, timeout);
        // 处理这n个就绪的事件
        for (int i = 0; i < n; ++i)
        {
            int fd = _revs[i].data.fd;
            uint32_t events = _revs[i].events;

            // 将所有的异常都转化为读写异常，交由后续的读写中处理异常
            if ((events & EPOLLERR) || (events & EPOLLHUP))
                events |= (EPOLLIN | EPOLLOUT);
            // 读事件处理
            if ((events & EPOLLIN) && ConnIsExists(fd))
                connections[fd]->_recv(connections[fd]);
            // 写事件处理
            if ((events & EPOLLOUT) && ConnIsExists(fd))
                connections[fd]->_send(connections[fd]);
        }
    }

    // 将事件放入_conn和epoll管理
    void AddConnection(int fd, uint32_t events, std::string clientIp = "127.0.0.1", uint16_t clientPort = 8888)
    {
        // ET模式需要设置非阻塞
        if (events & EPOLLET)
            Util::SetNoBlock(fd);
        Connection *conn = new Connection(fd, clientIp, clientPort);
        // 连接事件，只绑定获取连接的方法
        if (fd == _listensock.Fd())
            conn->Rigster(std::bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        else
        {
            // 普通事件，读写异常都绑定
            conn->Rigster(std::bind(&EpollServer::Recver, this, std::placeholders::_1),
                          std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                          std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
        }
        conn->_events = events;
        conn->_R = this;
        // 添加到Connection集合中
        connections.insert(std::pair<int, Connection *>(fd, conn));
        // 添加到epoll模型中
        bool r = _epoller.AddMovEvent(fd, events, EPOLL_CTL_ADD);
        assert(r); // 认为AddMovEvent不会出错
        (void)r;   // 防止r被优化掉
    }

    // 获取连接
    void Accepter(Connection *conn)
    {
        do
        {
            int err = 0;
            // 1. 新连接到来
            // logMessage(Debug, "get a new link ...");
            std::string clientip;
            uint16_t clientport;
            int sock = _listensock.Accept(&clientip, &clientport, &err);
            if (sock > 0)
            {
                logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);
                // 1.1 此时在这里，我们能不能进行recv/read ? 不能，只有epoll知道sock上面的事件情况，将sock添加到epoll中
                AddConnection(sock, EPOLLIN | EPOLLET, clientip, clientport);
            }
            else
            {
                //连接全部获取
                if (err == EAGAIN || err == EWOULDBLOCK)
                    break;
                else if (err == EINTR)
                    continue;//信号终止
                else
                {
                    //获取连接出错
                    logMessage(Warning, "errstring : %s, errcode: %d", strerror(err), err);
                    continue;
                }
            }
        } while (conn->_events & EPOLLET);

        logMessage(Debug, "accepter done ...");

    }

    // 读事件处理
    void Recver(Connection *conn)
    {
        do
        {
            char buffer[1024];
            size_t n = recv(conn->_fd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                conn->inbuffer += buffer;
                std::string request;
                // 解析是否有完整的报文
                int len = ParsePackage(conn->inbuffer, &request);
                if (len < 0) // 没有完整的报文，还需要继续读
                    continue;
                else
                {
                    // 拿到了完整的报文。先反序列化
                    request = RemoveHead(len, request);
                    Request req;
                    req.Deserialize(request);
                    _func(conn, req); // 回调函数
                }
            }
            else if (n == 0)
            {
                // 没有数据
                conn->_except(conn);
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 数据读完
                else if (errno == EINTR)
                    continue; // 信号终止
                else
                {
                    conn->_except(conn);
                    break;
                }
            }
        } while (conn->_events & EPOLLET);
    }

    void Sender(Connection *conn)
    {
        do
        {
            int n = send(conn->_fd, conn->outbuffer.c_str(), conn->outbuffer.size(), 0);
            if (n > 0)
            {
                conn->outbuffer.erase(0, n); // 去除发送缓冲区已经发送的数据
                if (conn->outbuffer.empty())
                {
                    EnableReadWrite(conn, true, false); // 数据已经发完
                    break;
                }
                else
                    EnableReadWrite(conn, true, true); // 数据还没发完
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 数据发完
                else if (errno == EINTR)
                    continue; // 信号终止
                else
                {
                    conn->_except(conn);
                    break;
                }
            }
        } while (conn->_events & EPOLLET);
    }

    void Excepter(Connection *conn)
    {
        logMessage(Debug, "Excepter..., fd: %d, clientinfo: [%s:%d]", conn->_fd, conn->_clientIp.c_str(), conn->_clientPort);
    }

    // 判断该文件描述符是否有效
    bool ConnIsExists(int fd)
    {
        return connections.find(fd) != connections.end();
    }

    // 根据读写使能修改epoll模型
    bool EnableReadWrite(Connection *conn, bool readable, bool writeable)
    {
        // 根据readable和writeable设置关心的事件
        conn->_events = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0));
        return _epoller.AddMovEvent(conn->_fd, conn->_events, EPOLL_CTL_MOD);
    }

    ~EpollServer()
    {
        _listensock.Close();
        _epoller.Close();
    }

private:
    uint16_t _port;                                    // 端口号
    Sock _listensock;                                  // 监听套接字
    Epoller _epoller;                                  // epoll模型
    struct epoll_event _revs[gnum];                    // 就绪事件集合
    func_t _func;                                      // 回调函数
    std::unordered_map<int, Connection *> connections; // 连接管理
};